﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace ShenGu.Script
{
    public class TemplateParser
    {
        public const string DefaultOutputFuncName = "__out", DefaultWriteFuncName = "__write";
        public const string KEY_Executor = "@@Executor";
        private readonly static Dictionary<string, ParseNodeCallback> dicSysNodeParsers;
        private IScriptBuilder builder;
        private string text;
        private int index, offset, length;
        private int nowrapCounter;
        private Dictionary<string, ParseNodeCallback> dicNodeParsers, dicTempNodeParsers;
        private TemplateNodeInfo stack, current;
        private readonly static CheckChildCallback switchChildCheck = new CheckChildCallback(CheckSwitchChildren);
        private readonly static ParseNodeCallback componentNodeParse = new ParseNodeCallback(ParseComponentNode);

        static TemplateParser()
        {
            dicSysNodeParsers = new Dictionary<string, ParseNodeCallback>(StringComparer.OrdinalIgnoreCase);
            dicSysNodeParsers.Add("if", new ParseNodeCallback(ParseIf));
            dicSysNodeParsers.Add("else", new ParseNodeCallback(ParseElse));
            dicSysNodeParsers.Add("for", new ParseNodeCallback(ParseFor));
            dicSysNodeParsers.Add("while", new ParseNodeCallback(ParseWhile));
            dicSysNodeParsers.Add("do", new ParseNodeCallback(ParseDoWhile));
            dicSysNodeParsers.Add("switch", new ParseNodeCallback(ParseSwitch));
            dicSysNodeParsers.Add("case", new ParseNodeCallback(ParseCase));
            dicSysNodeParsers.Add("default", new ParseNodeCallback(ParseDefault));
            dicSysNodeParsers.Add("break", new ParseNodeCallback(ParseBreak));
            dicSysNodeParsers.Add("continue", new ParseNodeCallback(ParseContinue));
            dicSysNodeParsers.Add("line", new ParseNodeCallback(ParseLine));
            dicSysNodeParsers.Add(string.Empty, new ParseNodeCallback(ParseCode));
            dicSysNodeParsers.Add("code", new ParseNodeCallback(ParseCode));
            dicSysNodeParsers.Add("try", new ParseNodeCallback(ParseTry));
            dicSysNodeParsers.Add("catch", new ParseNodeCallback(ParseCatch));
            dicSysNodeParsers.Add("finally", new ParseNodeCallback(ParseFinally));
            dicSysNodeParsers.Add("export", new ParseNodeCallback(ParseExport));
            dicSysNodeParsers.Add("import", new ParseNodeCallback(ParseImport));
            dicSysNodeParsers.Add("debugger", new ParseNodeCallback(ParseDebugger));
            dicSysNodeParsers.Add("nowrap", new ParseNodeCallback(ParseNoWrap));
            dicSysNodeParsers.Add("component", new ParseNodeCallback(ParseComponent));
            dicSysNodeParsers.Add("function", new ParseNodeCallback(ParseFunction));
            dicSysNodeParsers.Add("invoke", new ParseNodeCallback(ParseInvoke));
            dicSysNodeParsers.Add("slot", new ParseNodeCallback(ParseSlot));
            dicSysNodeParsers.Add("template", new ParseNodeCallback(ParseTemplate));
        }

        #region 节点处理方法

        private static void ParseInvoke(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
            {
                string id = CheckGetAttrValue(parser, info, "invoke", "id");
                string code = info.GetAttrValue("code");
                if (code == null) code = string.Empty;
                builder.AppendScript(string.Format("{0}({1});\r\n", id, code));
            }
        }

        private static void ParseComponentNode(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.WholeElement || info.NodeType == TemplateNodeType.Element)
            {
                string argsValue = null;
                if (info.HasAttrs)
                    argsValue = CombineArgusObject(info.Attrs);
                if (string.IsNullOrEmpty(argsValue))
                    argsValue = "null";
                if (info.NodeType == TemplateNodeType.WholeElement)
                    builder.AppendScript(string.Format("{0}({1})\r\n", info.Name, argsValue));
                else
                {
                    SlotScriptBuilder newBuilder = new SlotScriptBuilder();
                    IScriptBuilder oldBuilder = parser.SuspendBuilder(newBuilder);

                    List<IScriptBuilder> builderStack = new List<IScriptBuilder>();
                    builderStack.Add(oldBuilder);

                    Dictionary<string, SlotScriptBuilder> dicTemplates = new Dictionary<string, SlotScriptBuilder>();
                    dicTemplates.Add("__default", newBuilder);

                    info.SetValue("ArgsValue", argsValue);
                    info.SetValue("BuilderStack", builderStack);
                    info.SetValue("TemplateDict", dicTemplates);
                }
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
            {
                string id = info.Name;
                string argsValue = info.GetValue<string>("ArgsValue");
                string code = info.GetAttrValue("code");
                if (code == null) code = string.Empty;

                List<IScriptBuilder> builderStack = info.GetValue<List<IScriptBuilder>>("BuilderStack");
                builder = builderStack[0];
                parser.ResumeBuilder(builder);

                Dictionary<string, SlotScriptBuilder> dicTemplates = info.GetValue<Dictionary<string, SlotScriptBuilder>>("TemplateDict");
                SlotScriptBuilder defaultBuilder = dicTemplates["__default"];
                if (dicTemplates.Count == 1 && defaultBuilder.ScriptLength == 0)
                    builder.AppendScript(string.Format("{0}({1});\r\n", id, argsValue));
                else
                {
                    StringBuilder sb = new StringBuilder();
                    sb.Append(id);
                    sb.Append('(');
                    sb.Append(argsValue);
                    sb.Append(",{\r\n");
                    bool isFirst = true;
                    foreach (KeyValuePair<string, SlotScriptBuilder> kv in dicTemplates)
                    {
                        if (kv.Value.ScriptLength > 0)
                        {
                            if (isFirst) isFirst = false;
                            else sb.Append(",\r\n");
                            sb.Append(kv.Key + ":(");
                            if (!string.IsNullOrEmpty(kv.Value.Arguments))
                                sb.Append(kv.Value.Arguments);
                            sb.Append(") => {\r\n");
                            sb.Append(kv.Value.ToScript());
                            sb.Append("\r\n}");
                        }
                    }
                    sb.Append("\r\n});\r\n");
                    builder.AppendScript(sb.ToString());
                }
            }
        }

        private static void ParseFunction(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                string id = info.GetAttrValue("id");
                string code = info.GetAttrValue("code");
                if (id == null) id = string.Empty;
                if (code == null) code = string.Empty;
                builder.AppendScript(string.Format("function {0}({1})", id, code));
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
            {
                AppendBrace(builder, false);
            }
        }

        private static void ParseComponent(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
            {
                string defineTag = info.GetAttrValue("define");
                if (!string.IsNullOrEmpty(defineTag))
                {
                    string[] tags = defineTag.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string t in tags)
                        parser.RegisterComponentNode(t);
                    info.SetValue("IgnoreBody", null);
                }
                else
                {
                    string id = info.GetAttrValue("id");
                    if (info.HasAttrValue("export"))
                    {
                        if (string.IsNullOrEmpty(id))
                            builder.AppendScript("export default function(__args, __slots)");
                        else
                            builder.AppendScript(string.Format("export function {0}(__args, __slots)", id));
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(id))
                            parser.ThrowError("组件定义，必须指定Id。");
                        builder.AppendScript(string.Format("function {0}(__args, __slots)", id));
                    }
                    AppendBrace(builder, true);
                    if (!string.IsNullOrEmpty(id))
                        parser.RegisterComponentNode(id);
                    if (info.HasAttrs)
                    {
                        List<string> argList = null;
                        foreach (KeyValuePair<string, string> kv in info.Attrs)
                        {
                            if (kv.Key.Length > 1 && kv.Key[0] == ':')
                            {
                                string argName = kv.Key.Substring(1);
                                builder.AppendScript(string.Format("var {0}={1};\r\n", argName, kv.Value == null ? "undefined" : kv.Value));
                                if (argList == null)
                                    argList = new List<string>();
                                argList.Add(argName);
                            }
                        }
                        if (argList != null)
                        {
                            builder.AppendScript("if (__args)");
                            AppendBrace(builder, true);
                            foreach (string argName in argList)
                                builder.AppendScript(string.Format("  if (__args.{0} !== undefined) {0}=__args.{0};\r\n", argName));
                            AppendBrace(builder, false);
                        }
                    }
                }
            }
            if ((info.NodeType == TemplateNodeType.EndElement || info.NodeType == TemplateNodeType.WholeElement) && !info.HasValue("IgnoreBody"))
            {
                AppendBrace(builder, false);
            }
        }

        private void RegisterComponentNode(string nodeName)
        {
            RegisterTempNode(nodeName, new ParseNodeCallback(componentNodeParse));
        }

        private static void ParseSlot(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.WholeElement || info.NodeType == TemplateNodeType.Element)
            {
                string id = info.GetAttrValue("id");
                string code = info.GetAttrValue("code");
                if (string.IsNullOrEmpty(id))
                    id = "__default";
                builder.AppendScript(string.Format("if (__slots && __slots['{0}'])\r\n   __slots['{0}']({1});\r\n", id, code != null ? code : string.Empty));
                if (info.NodeType == TemplateNodeType.Element)
                    builder.AppendScript("else {\r\n");
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
            {
                AppendBrace(builder, false);
            }
        }

        private static void ParseTemplate(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            TemplateNodeInfo parent = info.Parent;
            if (parent == null || parser.dicTempNodeParsers == null || !parser.dicTempNodeParsers.ContainsKey(parent.Name))
                parser.ThrowError("“template”节点必须是组件节点的直属下级节点。");

            if (info.NodeType == TemplateNodeType.Element)
            {
                string id = info.GetAttrValue("id");
                if (string.IsNullOrEmpty(id))
                    id = "__default";

                List<IScriptBuilder> builderStack = parent.GetValue<List<IScriptBuilder>>("BuilderStack");
                Dictionary<string, SlotScriptBuilder> dicTemplates = parent.GetValue<Dictionary<string, SlotScriptBuilder>>("TemplateDict");
                SlotScriptBuilder newBuilder;
                if (!dicTemplates.TryGetValue(id, out newBuilder))
                    dicTemplates[id] = newBuilder = new SlotScriptBuilder();
                string code = info.GetAttrValue("code");
                if (!string.IsNullOrEmpty(code))
                    newBuilder.Arguments = code;
                IScriptBuilder oldBuilder = parser.SuspendBuilder(newBuilder);
                builderStack.Add(oldBuilder);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
            {
                List<IScriptBuilder> builderStack = parent.GetValue<List<IScriptBuilder>>("BuilderStack");
                builderStack.RemoveAt(builderStack.Count - 1);
            }
        }

        private static void ParseNoWrap(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
                parser.nowrapCounter++;
            else if (info.NodeType == TemplateNodeType.EndElement)
                parser.nowrapCounter--;
        }

        private static void ParseDebugger(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
                builder.AppendScript("\r\ndebugger;\r\n");
        }

        private static string CombineArgusObject(Dictionary<string, string> dic)
        {
            StringBuilder sbArgus = null;
            if (dic != null)
            {
                foreach (KeyValuePair<string, string> kv in dic)
                {
                    string key = kv.Key;
                    if (key.Length > 1 && key[0] == ':')
                    {
                        if (sbArgus == null)
                        {
                            sbArgus = new StringBuilder();
                            sbArgus.Append('{');
                        }
                        else sbArgus.Append(',');
                        sbArgus.Append(key.Substring(1));
                        sbArgus.Append(':');
                        sbArgus.Append(kv.Value);
                    }
                }
                if (sbArgus != null) sbArgus.Append('}');
            }
            return sbArgus != null ? sbArgus.ToString() : string.Empty;
        }

        private static void ParseImport(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
            {
                string code = CheckGetAttrValue(parser, info, "import", "code");
                string from = CheckGetAttrValue(parser, info, "import", "from");
                if (from.Contains("\""))
                    from = from.Replace("\"", "\\\"");
                builder.AppendScript(string.Format("import {0} from \"{1}\";\r\n", code, from));
            }
        }

        private static void ParseExport(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
            {
                string code = CheckGetAttrValue(parser, info, "export", "code");
                string from = info.GetAttrValue("from");
                builder.AppendScript(string.Format("export {0}", code));
                if (!string.IsNullOrEmpty(from))
                {
                    if (from.Contains("\""))
                        from = from.Replace("\"", "\\\"");
                    builder.AppendScript(string.Format(" from \"{0}\"", from));
                }
                builder.AppendScript(";\r\n");
            }
        }

        private static void AppendBrace(IScriptBuilder builder, bool isStarted)
        {
            builder.AppendScript(isStarted ? "\r\n{\r\n" : "\r\n}\r\n");
        }

        private static void ThrowAttrNotExist(TemplateParser parser, string nodeName, string attrName)
        {
            parser.ThrowError("“{0}”节点必须包含“{1}”属性。", nodeName, attrName);
        }

        private static string CheckGetAttrValue(TemplateParser parser, TemplateNodeInfo info, string nodeName, string attrName)
        {
            string code = info.GetAttrValue(attrName);
            if (string.IsNullOrEmpty(code))
                ThrowAttrNotExist(parser, nodeName, attrName);
            return code;
        }

        private static void ParseIf(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                string code = CheckGetAttrValue(parser, info, "if", "code");
                builder.AppendScript(string.Format("if ({0})", code));
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
                AppendBrace(builder, false);
        }

        private static void ParseElse(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                TemplateNodeInfo prev = info.Previous;
                if (prev == null
                    || (prev.Name != "if" && prev.Name != "else")
                    || (prev.Name == "else" && prev.GetAttrValue("if") == null))
                    parser.ThrowError("“else”节点必须在“if”或“else if”节点之后。");
                string code = info.GetAttrValue("if");
                if (code == null)
                    builder.AppendScript("else");
                else
                    builder.AppendScript(string.Format("else if ({0})", code));
                AppendBrace(builder, true);
            }
            else
                AppendBrace(builder, false);
        }

        private static void ParseFor(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                string code = CheckGetAttrValue(parser, info, "for", "code");
                builder.AppendScript(string.Format("for ({0})", code));
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
                AppendBrace(builder, false);
        }

        private static void ParseWhile(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            string code = CheckGetAttrValue(parser, info, "while", "code");
            if (info.NodeType == TemplateNodeType.Element)
            {
                builder.AppendScript(string.Format("while ({0})", code));
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
                AppendBrace(builder, false);
        }

        private static void ParseDoWhile(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                builder.AppendScript("do");
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
            {
                string code = CheckGetAttrValue(parser, info, "do...while", "while");
                AppendBrace(builder, false);
                builder.AppendScript(string.Format("while({0});\r\n", code));
            }
        }

        private static void ParseSwitch(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                string code = CheckGetAttrValue(parser, info, "switch", "code");
                builder.AppendScript(string.Format("switch ({0})", code));
                AppendBrace(builder, true);
                info.CheckChildHandler = switchChildCheck;
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
                AppendBrace(builder, false);
        }

        private static void CheckSwitchChildren(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo parent, TemplateNodeInfo child)
        {
            switch (child.NodeType)
            {
                case TemplateNodeType.Element:
                case TemplateNodeType.EndElement:
                case TemplateNodeType.WholeElement:
                    if (child.Name == "case" || child.Name == "default") return;
                    break;
            }
            parser.ThrowError("“switch”的一级子节点，必须是“case”节点");
        }

        private static void ParseCase(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.Parent == null || info.Parent.NodeType != TemplateNodeType.Element || info.Parent.Name != "switch")
                parser.ThrowError("“case”必须包含在“switch”内部。");
            switch (info.NodeType)
            {
                case TemplateNodeType.Element:
                    {
                        string code = CheckGetAttrValue(parser, info, "case", "code");
                        builder.AppendScript(string.Format("case {0}:", code));
                        AppendBrace(builder, true);
                        break;
                    }
                case TemplateNodeType.EndElement:
                    {
                        string str = info.GetAttrValue("ignoreBreak");
                        if (str == null || str.Trim() != "true")
                            builder.AppendScript("break;");
                        AppendBrace(builder, false);
                        break;
                    }
                case TemplateNodeType.WholeElement:
                    {
                        string code = CheckGetAttrValue(parser, info, "case", "code");
                        builder.AppendScript(string.Format("case {0}:\r\n", code));
                        break;
                    }
            }
        }

        private static void ParseDefault(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                if (info.Parent == null || info.Parent.NodeType != TemplateNodeType.Element || info.Parent.Name != "switch")
                    parser.ThrowError("“default”必须包含在“switch”内部。");
                builder.AppendScript("default:");
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
            {
                string str = info.GetAttrValue("ignoreBreak");
                if (str == null || str.Trim() != "true")
                    builder.AppendScript("break;");
                AppendBrace(builder, false);
            }
        }

        private static void ParseBreak(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
                builder.AppendScript("break;\r\n");
        }

        private static void ParseContinue(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
                builder.AppendScript("continue;\r\n");
        }

        private static void ParseLine(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element || info.NodeType == TemplateNodeType.WholeElement)
            {
                string code = CheckGetAttrValue(parser, info, "line", "code");
                builder.AppendScript(code);
                if (!code.EndsWith(";")) builder.AppendScript(";\r\n");
                else builder.AppendScript("\r\n");
            }
        }

        private static void ParseCode(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                string content = parser.ReadNodeContent();
                builder.AppendScript(content);
                builder.AppendScript("\r\n");
            }
        }

        private static void ParseTry(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                builder.AppendScript("try");
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
                AppendBrace(builder, false);
        }

        private static void ParseCatch(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                if (info.Previous == null || info.Parent.Name != "try")
                    parser.ThrowError("“catch”必须在“try”之后。");
                string varName = CheckGetAttrValue(parser, info, "catch", "var");
                builder.AppendScript(string.Format("catch ({0})", varName));
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
                AppendBrace(builder, false);
        }

        private static void ParseFinally(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info)
        {
            if (info.NodeType == TemplateNodeType.Element)
            {
                if (info.Previous == null || (info.Parent.Name != "try" && info.Parent.Name != "catch"))
                    parser.ThrowError("“catch”必须在“try”或者“catch”之后。");
                builder.AppendScript("finally");
                AppendBrace(builder, true);
            }
            else if (info.NodeType == TemplateNodeType.EndElement)
                AppendBrace(builder, false);
        }

        #endregion

        #region 私有方法

        private IScriptBuilder SuspendBuilder(IScriptBuilder newBuilder)
        {
            IScriptBuilder oldBuilder = builder;
            builder = newBuilder;
            return oldBuilder;
        }

        public void ResumeBuilder(IScriptBuilder oldBuilder)
        {
            this.builder = oldBuilder;
        }

        private static bool IsWhiteSpace(char ch)
        {
            switch (ch)
            {
                case ' ':
                case '\t':
                case '\r':
                case '\n':
                    return true;
            }
            return false;
        }

        private static bool IsNameChar(char ch)
        {
            return (ch >= 'a' && ch <= 'z')
                || (ch >= 'A' && ch <= 'Z')
                || (ch >= '0' && ch <= '9')
                || ch == '-' || ch == '_' || ch == ':';
        }

        private void SetCurrent(TemplateNodeInfo info)
        {
            if (stack != null)
            {
                CheckChildCallback check = stack.CheckChildHandler;
                if (check != null) check(this, builder, stack, info);
            }
            info.Previous = current;
            info.Parent = stack;
            current = info;
        }

        private void PushCurrent(TemplateNodeInfo info)
        {
            if (stack != null)
            {
                CheckChildCallback check = stack.CheckChildHandler;
                if (check != null) check(this, builder, stack, info);
            }
            info.Previous = current;
            info.Parent = stack;
            current = null;
            stack = info;
        }

        private TemplateNodeInfo PopCurrent(string nodeName)
        {
            if (stack != null && stack.NodeType == TemplateNodeType.Element && stack.Name == nodeName)
            {
                stack.NodeType = TemplateNodeType.EndElement;
                current = stack;
                stack = stack.Parent;
                return current;
            }
            ThrowError("读取到不匹配的结束节点：" + nodeName);
            return null;
        }

        private void ThrowError(string errorMsg, params object[] tempateArgus)
        {
            if (tempateArgus != null && tempateArgus.Length > 0) errorMsg = string.Format(errorMsg, tempateArgus);
            throw new TemplateParseException(this, index, errorMsg, null);
        }

        private int LocateName(int i)
        {
            for (int j = i; j < length; j++)
            {
                if (!IsNameChar(text[j])) return j - i;
            }
            return length - i;
        }

        private string ReadName(int i, int len)
        {
            return len == 0 ? string.Empty : text.Substring(i, len);
        }

        private int LocateStringValue(int i)
        {
            char ch = text[i];
            if (ch == '\'' || ch == '"')
            {
                for (int j = i + 1; j < length; j++)
                {
                    if (ch == text[j]) return j - i + 1;
                }
            }
            return -1;
        }

        private string ReadStringValue(int i, int len)
        {
            if (len == 2) return string.Empty;
            return text.Substring(i + 1, len - 2);
        }

        private void SkipWhiteSpace()
        {
            int len = text.Length;
            for (; offset < len; offset++)
            {
                if (!IsWhiteSpace(text[offset])) break;
            }
        }

        private void AcceptOffset()
        {
            this.index = this.offset;
        }

        private void AddTextNode(int textIndex, int textOffset)
        {
            TemplateNodeInfo info = new TemplateNodeInfo(TemplateNodeType.Text, null);
            info.Index = textIndex;
            info.Length = textOffset - textIndex;
            SetCurrent(info);
            builder.AppendScript(string.Format("{0}({1}, {2});\r\n", OutputFunctionName, info.Index, info.Length));
        }

        private void ParseText(int textIndex, int textOffset)
        {
            if (textIndex < textOffset)
            {
                if (nowrapCounter > 0)
                {
                    int i = textIndex;
                    while (i < textOffset)
                    {
                        int i2 = text.IndexOf('\n', i, textOffset - i);
                        if (i2 >= 0)
                        {
                            int i3 = i2;
                            if (i3 > 0 && text[i3 - 1] == '\r')
                                i3--;
                            if (i < i3)
                                AddTextNode(i, i3);
                            i = i2 + 1;
                        }
                        else
                        {
                            if (i < textOffset)
                                AddTextNode(i, textOffset);
                            break;
                        }
                    }
                }
                else
                    AddTextNode(textIndex, textOffset);
            }
        }

        private void ParseNode(bool isEnd, bool isWhiteSpaceHead)
        {
            int prevIndex = index;
            int prevOffset = offset;

            AcceptOffset();
            if (isEnd) offset += 3;
            else offset += 2;

            int nameLen = LocateName(offset);
            string name = ReadName(offset, nameLen);
            offset += nameLen;

            TemplateNodeInfo info = null;
            int nodeOpt = 0;
            char ch;
            do
            {
                SkipWhiteSpace();
                if (offset >= length) ThrowError("节点“{0}”没有结束符", name);
                ch = text[offset];
                if (ch == '/')
                {
                    if (++offset >= length || text[offset] != '>' || isEnd) ThrowError("节点“{0}”中读取到错误的符号：{1}", name, ch);
                    offset++;
                    if (info == null)
                        info = new TemplateNodeInfo(TemplateNodeType.WholeElement, name);
                    else
                        info.NodeType = TemplateNodeType.WholeElement;
                    nodeOpt = 3;
                    break;
                }
                else if (ch == '>')
                {
                    offset++;
                    if (isEnd)
                    {
                        if (info != null) ThrowError("结束节点“{0}”不允许存在属性", name);
                        nodeOpt = 2;
                        break;
                    }
                    if (info == null)
                        info = new TemplateNodeInfo(TemplateNodeType.Element, name);
                    nodeOpt = 1;
                    break;
                }
                int attrLen = LocateName(offset);
                if (attrLen == 0) ThrowError("节点“{0}”的属性中，存在特殊字符：{1}", name, ch);
                string attrName = ReadName(offset, attrLen);
                offset += attrLen;
                SkipWhiteSpace();
                string attrValue;
                if (text[offset] == '=')
                {
                    if (offset >= length || text[offset] != '=') ThrowError("节点“{0}”读取不到属性“{1}”的值", name, attrName);
                    offset++;
                    SkipWhiteSpace();
                    attrLen = LocateStringValue(offset);
                    if (attrLen < 0) ThrowError("节点“{0}”属性“{1}”的值，必须以单引号或双引号开始。", name, attrName);
                    attrValue = ReadStringValue(offset, attrLen);
                    offset += attrLen;
                }
                else
                    attrValue = null;
                if (info == null)
                    info = new TemplateNodeInfo(TemplateNodeType.Element, name);
                info.Attrs.Add(attrName, attrValue);
            } while (true);

            if (isWhiteSpaceHead)
            {
                if (nodeOpt != 2 || !name.Equals("nowrap", StringComparison.OrdinalIgnoreCase))
                {
                    int afterOffset = CalcAfterOffset(offset);
                    if (afterOffset > 0)
                        offset = afterOffset;
                }
            }
            else
            {
                int beforeOffset = CalcBeforeOffset(prevOffset);
                int afterOffset = CalcAfterOffset(offset);
                if (beforeOffset >= 0 && afterOffset > 0)  // 当前节点单独一行时，整行不输出
                {
                    ParseText(prevIndex, beforeOffset);
                    if (nodeOpt != 2 || !name.Equals("nowrap", StringComparison.OrdinalIgnoreCase))
                        offset = afterOffset;
                }
                else
                    ParseText(prevIndex, prevOffset);
            }
            switch (nodeOpt)
            {
                case 1:
                    PushCurrent(info);
                    break;
                case 2:
                    info = PopCurrent(name);
                    break;
                case 3:
                    SetCurrent(info);
                    break;
            }

            ParseNodeCallback callback;
            if ((dicTempNodeParsers != null && dicTempNodeParsers.TryGetValue(info.Name, out callback))
                || (dicNodeParsers != null && dicNodeParsers.TryGetValue(info.Name, out callback))
                || dicSysNodeParsers.TryGetValue(info.Name, out callback))
                callback(this, builder, info);
            AcceptOffset();
        }

        private void ParseLine(bool fullWrite)
        {
            int firstIndex = offset;
            for (; offset < length; offset++)
            {
                char ch = text[offset];
                if (ch == '}')
                {
                    string script = ReadName(firstIndex, offset - firstIndex);
                    builder.AppendScript(string.Format("{0}({1},{2});\r\n", WriteFunctionName, script, fullWrite ? "true" : "false"));
                    offset++;
                    break;
                }
            }
            index = offset;
        }

        private void RegisterTempNode(string nodeName, ParseNodeCallback callback)
        {
            if (dicTempNodeParsers == null) dicTempNodeParsers = new Dictionary<string, ParseNodeCallback>(StringComparer.OrdinalIgnoreCase);
            dicTempNodeParsers[nodeName] = callback;
        }

        #endregion

        #region 重载方法

        protected virtual string OutputFunctionName { get { return DefaultOutputFuncName; } }

        protected virtual string WriteFunctionName { get { return DefaultWriteFuncName; } }

        #endregion

        #region 公共方法

        public void Register(string nodeName, ParseNodeCallback callback)
        {
            if (dicNodeParsers == null) dicNodeParsers = new Dictionary<string, ParseNodeCallback>(StringComparer.OrdinalIgnoreCase);
            dicNodeParsers.Add(nodeName, callback);
        }

        private int CalcBeforeOffset(int loc)
        {
            bool doSkip = false;
            int i = loc - 1;
            for (; i >= 0; i--)
            {
                char ch = text[i];
                if (ch == ' ' || ch == '\t')
                {

                }
                else
                {
                    if (ch == '\r' || ch == '\n')
                        doSkip = true;
                    break;
                }
            }
            if (doSkip)
                return i + 1;
            else if (i == 0)
                return 0;
            return -1;
        }

        private int CalcAfterOffset(int loc)
        {
            int len = text.Length;
            int i = loc + 1;
            bool doSkip = false;
            for (; i < len; i++)
            {
                char ch = text[i];
                if (ch == ' ' || ch == '\t' || ch == '\r')
                {

                }
                else
                {
                    if (ch == '\n')
                    {
                        i++;
                        doSkip = true;
                    }
                    else if (ch == '<' && i + 2 < len && (text[i + 1] == '@' || (text[i + 1] == '/' && text[i + 2] == '@')))
                        doSkip = true;
                    break;
                }
            }
            if (doSkip)
                return i;
            if (i == len)
                return len;
            return -1;
        }

        private void InternalParse(IScriptBuilder builder, string script)
        {
            this.builder = builder;
            this.text = script;

            index = 0;
            length = text.Length;
            int len = text.Length - 2;
            bool isWhitespace = true;
            for (offset = 0; offset < len;)
            {
                char ch = text[offset];
                if (ch == '<')
                {
                    char ch1 = text[offset + 1];
                    if (ch1 == '@')
                    {
                        ParseNode(false, isWhitespace);
                        isWhitespace = true;
                        continue;
                    }
                    else if (ch1 == '/' && text[offset + 2] == '@')
                    {
                        ParseNode(true, isWhitespace);
                        isWhitespace = true;
                        continue;
                    }
                }
                else if (ch == '@')
                {
                    char ch1 = text[offset + 1];
                    bool fullWrite = false;
                    if (ch1 == '@')
                    {
                        fullWrite = true;
                        ch1 = text[offset + 2];
                    }
                    if (ch1 == '{')
                    {
                        ParseText(index, offset);
                        AcceptOffset();
                        isWhitespace = true;
                        offset += 2;
                        if (fullWrite) offset++;
                        ParseLine(fullWrite);
                        continue;
                    }
                }
                if (!IsWhiteSpace(ch)) isWhitespace = false;
                offset++;
            }
            offset = length;
            ParseText(index, offset);
            AcceptOffset();
        }

        public void Parse(IScriptBuilder builder, string script, bool doCheck)
        {
            dicTempNodeParsers = null;
            InternalParse(builder, script);
            if (doCheck)
            {
                string str = builder.ToScript();
                ScriptParser.Parse(str);
            }
        }

        public string Parse(string script, bool doCheck)
        {
            StringScriptBuilder builder = new StringScriptBuilder();
            Parse(builder, script, doCheck);
            return builder.ToString();
        }

        public void Execute(ScriptParser parser, ScriptContext context, ITemplateExecutor executor)
        {
            ScriptExecutorProxy proxy = new ScriptExecutorProxy(executor);
            context.SetCacheValue(KEY_Executor, executor);
            context.ImportManager = proxy;
            context.AddValue(OutputFunctionName, new OutputFunctionCallback(proxy.Output));
            context.AddValue(WriteFunctionName, new WriteFunctionCallback(executor.Write));
            parser.Execute(context);
        }

        public void Execute(string script, ScriptContext context, ITemplateExecutor executor)
        {
            ScriptParser parser = ScriptParser.Parse(script);
            Execute(parser, context, executor);
        }

        public string ReadNodeContent()
        {
            TemplateNodeInfo info = stack;
            if (info != null)
            {
                int len = length - 5;
                int firstIndex = offset, lastIndex = offset;
                for (; offset < len; offset++)
                {
                    char ch = text[offset];
                    if (ch == '<' && text[offset + 1] == '/' && text[offset + 2] == '@')
                    {
                        lastIndex = offset;
                        offset += 3;
                        int nameLen = LocateName(offset);
                        string name = ReadName(offset, nameLen);
                        if (info.Name == name)
                        {
                            offset += nameLen;
                            if (offset < length && text[offset] == '>')
                            {
                                offset++;
                                PopCurrent(name);
                                return ReadName(firstIndex, lastIndex - firstIndex);
                            }
                        }
                    }
                }
                ThrowError("读取不到节点“{0}”对应的结束节点。", info.Name);
            }
            return null;
        }

        public string Script { get { return text; } }

        #endregion

        #region 内部类

        delegate void OutputFunctionCallback(ScriptContext context, IImportSource importSource, int index, int length);

        delegate void WriteFunctionCallback(ScriptContext context, IScriptObject value, bool fullWrite);

        class ScriptExecutorProxy : IImportSourceManager
        {
            private ITemplateExecutor executor;

            public ScriptExecutorProxy(ITemplateExecutor executor)
            {
                this.executor = executor;
            }

            public IImportSource GetSource(ScriptContext context, string path)
            {
                return executor.GetImportSource(context, path);
            }

            public void Output(ScriptContext context, IImportSource source, int index, int length)
            {
                object outputSource = source != null ? ((TemplateImportSource)source).OutputSource : executor.OutputSource;
                executor.Output(context, outputSource, index, length);
            }
        }

        class SlotScriptBuilder : StringScriptBuilder
        {
            public string Arguments { get; set; }
        }

        #endregion
    }

    #region 异常

    public class TemplateParseException : BaseScriptException
    {
        private TemplateParser parser;

        public TemplateParseException(TemplateParser parser, int charIndex, string errorMessage, Exception innerException) : base(charIndex, errorMessage, innerException)
        {
            this.parser = parser;
        }

        protected TemplateParseException(SerializationInfo info, StreamingContext context) : base(info, context) { }

        public TemplateParser Parser { get { return parser; } }
        public override string Script { get { return parser != null ? parser.Script : null; } }
    }

    #endregion

    #region 节点类定义

    public delegate void ParseNodeCallback(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo info);

    public delegate void CheckChildCallback(TemplateParser parser, IScriptBuilder builder, TemplateNodeInfo parent, TemplateNodeInfo child);

    public enum TemplateNodeType
    {
        Element, EndElement, WholeElement, Text
    }

    public class TemplateNodeInfo
    {
        private TemplateNodeType nodeType;
        private string name;
        private Dictionary<string, string> attrs;
        private int index, length;
        private TemplateNodeInfo parent, previous;
        private CheckChildCallback checkChild;
        private int state;
        private Dictionary<string, object> dicValues;

        internal TemplateNodeInfo(TemplateNodeType type, string name)
        {
            this.nodeType = type;
            this.name = name;
        }

        public TemplateNodeType NodeType
        {
            get { return nodeType; }
            internal set { nodeType = value; }
        }

        public string Name { get { return name; } }

        public Dictionary<string, string> Attrs
        {
            get
            {
                if (attrs == null) attrs = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                return attrs;
            }
        }

        public bool HasAttrs { get { return attrs != null && attrs.Count > 0; } }

        public bool HasAttrValue(string name)
        {
            return attrs != null && attrs.ContainsKey(name);
        }

        public string GetAttrValue(string name)
        {
            string result;
            if (attrs != null && attrs.TryGetValue(name, out result)) return result;
            return null;
        }

        public int Index
        {
            get { return index; }
            internal set { index = value; }
        }

        public int Length
        {
            get { return length; }
            internal set { length = value; }
        }

        public TemplateNodeInfo Parent
        {
            get { return parent; }
            internal set { parent = value; }
        }

        public TemplateNodeInfo Previous
        {
            get { return previous; }
            internal set { previous = value; }
        }

        public CheckChildCallback CheckChildHandler
        {
            get { return checkChild; }
            set { checkChild = value; }
        }

        public int Status
        {
            get { return state; }
            set { state = value; }
        }

        public bool HasValue(string key)
        {
            return dicValues != null && dicValues.ContainsKey(key);
        }

        public object GetValue(string key)
        {
            object result;
            if (dicValues != null && dicValues.TryGetValue(key, out result))
                return result;
            return null;
        }

        public T GetValue<T>(string key)
        {
            object result;
            if (dicValues != null && dicValues.TryGetValue(key, out result))
                return (T)result;
            return default(T);
        }

        public void SetValue(string key, object value)
        {
            if (dicValues == null)
                dicValues = new Dictionary<string, object>();
            dicValues[key] = value;
        }
    }

    #endregion

    #region 接口定义

    /// <summary>脚本生成器</summary>
    /// <remarks>用于将模板生成可执行的JavaScript语法</remarks>
    public interface IScriptBuilder
    {
        /// <summary>添加脚本</summary>
        void AppendScript(string script);

        /// <summary>生成脚本</summary>
        string ToScript();
    }

    /// <summary>模板生成器</summary>
    /// <remarks>提供模板生成相应的方法及属性</remarks>
    public interface ITemplateExecutor
    {
        /// <summary>传入Output的来源</summary>
        object OutputSource { get; }

        /// <summary>写入模板中的一部分内容（根据位置(<paramref name="index"/>)和长度(<paramref name="length"/>)）</summary>
        void Output(ScriptContext context, object source, int index, int length);

        /// <summary>写入值内容</summary>
        /// <param name="context"></param>
        /// <param name="value"></param>
        /// <param name="fullWrite">是否完整写入（完整写入为：@@{***}，否则为：@{***}）。<c>true</c>表示连undefined/null也显示相应的文本。</param>
        void Write(ScriptContext context, IScriptObject value, bool fullWrite);

        /// <summary>获取import的来源</summary>
        TemplateImportSource GetImportSource(ScriptContext context, string path);
    }

    /// <summary>模板的import来源</summary>
    public class TemplateImportSource : IImportSource
    {
        private string pathKey;
        private object outputSource;
        private ScriptParser parser;
        private IScriptObject result;

        protected TemplateImportSource(string pathKey, ScriptParser parser, object outputSource, IScriptObject result)
        {
            this.pathKey = pathKey;
            this.parser = parser;
            this.outputSource = outputSource;
            this.result = result;
        }

        /// <summary>根据模板的Parser创建</summary>
        /// <param name="pathKey"></param>
        /// <param name="parser"></param>
        /// <param name="outputSource"></param>
        /// <returns></returns>
        public static TemplateImportSource CreateByTemplate(string pathKey, ScriptParser parser, object outputSource)
        {
            if (pathKey == null)
                throw new ArgumentNullException(nameof(pathKey));
            if (parser == null)
                throw new ArgumentNullException(nameof(parser));
            if (outputSource == null)
                throw new ArgumentNullException(nameof(outputSource));
            return new TemplateImportSource(pathKey, parser, outputSource, null);
        }

        /// <summary>根据脚本的Parser创建</summary>
        /// <param name="pathKey"></param>
        /// <param name="parser"></param>
        /// <returns></returns>
        public static TemplateImportSource CreateByScript(string pathKey, ScriptParser parser)
        {
            if (pathKey == null)
                throw new ArgumentNullException(nameof(pathKey));
            if (parser == null)
                throw new ArgumentNullException(nameof(parser));
            return new TemplateImportSource(pathKey, parser, null, null);
        }

        /// <summary>根据结果创建</summary>
        /// <param name="pathKey"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static TemplateImportSource CreateByResult(string pathKey, IScriptObject result)
        {
            if (pathKey == null)
                throw new ArgumentNullException(nameof(pathKey));
            if (result == null)
                throw new ArgumentNullException(nameof(result));
            return new TemplateImportSource(pathKey, null, null, result);
        }

        public string PathKey { get { return pathKey; } }

        public ScriptParser Parser { get { return parser; } }

        public object OutputSource { get { return outputSource; } }

        public IScriptObject Result { get { return result; } }
    }

    #endregion

    #region 接口默认实现

    /// <summary>
    /// 脚本生成器的字符串实现
    /// </summary>
    public class StringScriptBuilder : IScriptBuilder
    {
        private StringBuilder buffer;

        public StringScriptBuilder()
        {
            this.buffer = new StringBuilder(128);
        }

        public void AppendScript(string script)
        {
            this.buffer.Append(script);
        }

        public int ScriptLength { get { return buffer.Length; } }

        public string ToScript()
        {
            return this.buffer.ToString();
        }

        public override string ToString()
        {
            return ToScript();
        }
    }

    /// <summary>
    /// 模板生成器的字符串实现
    /// </summary>
    public class StringTemplateExecutor : ITemplateExecutor
    {
        private StringBuilder builder;
        private string source;

        public object OutputSource { get { return source; } }

        public StringTemplateExecutor(string source)
        {
            this.source = source;
            this.builder = new StringBuilder(source.Length);
        }

        public void Output(ScriptContext context, object source, int index, int length)
        {
            builder.Append((string)source, index, length);
        }

        public void Write(ScriptContext context, IScriptObject value, bool fullWrite)
        {
            if (fullWrite || (value != ScriptUndefined.Value && value != ScriptNull.Value))
                builder.Append(value.ToValueString(context));
        }

        public override string ToString()
        {
            return builder.ToString();
        }

        protected virtual TemplateImportSource OnGetImportSource(ScriptContext context, string path)
        {
            return null;
        }

        public TemplateImportSource GetImportSource(ScriptContext context, string path)
        {
            return OnGetImportSource(context, path);
        }
    }

    #endregion
}
